#!-*-coding:utf-8-*-
"""
客户端协议
"""

from protocol import *

_ErrorCode = {
    ERROR_NAME_HAS_BEEN_REGISTERED: "该用户名已被注册!",
    ERROR_NAME_NOT_EXIST: "用户名不存在!",
    ERROR_PWD: "密码错误!",
    ERROR_PEER_NAME_NOT_EXIST: "对方用户名不存在!",
    ERROR_PEER_NOT_ONLINE: "对方不在线!",
    ERROR_NOT_JOIN_IN_ROOM: "你未加入房间!",
    ERROR_ACK_TIME_OUT: "超时!",
    ERROR_NOT_LOGIN: "你还未登录!",
    ERROR_PRIVATE_CHAT_TO_SELF: "不能私聊自己!",
    ERROR_REPEAT_LOGIN: "不能重复登录或同时登录多个账号!",
    ERROR_SUCH_USER_NAME_HAS_BEEN_LOGINED: "该用户名已登录!",
    ERROR_EXISTED_ROOM_NAME: "创建房间失败, 房间名已存在, 请选择其他房间名!",
    ERROR_NOT_IN_ROOM: "你未加入房间!",
    ERROR_HAVE_BEEN_IN_ROOM: "你已在房间中!",
    ERROR_REPEAT_ENTER_ROOM: "不能重复进入房间!",
    ERROR_ROOM_NAME_NOT_EXIST: "房间名不存在!",
    ERROR_GAME_INVALID_CHARACTER: "输入的字符无效或非给定的字符组合!",
    ERROR_GAME_INVLAID_EXPRESSION: "输入的表达式非法!",
    ERROR_NOT_IN_GAME_TIME: "当前不是游戏时间!",
    ERROR_GAME_NOT_IN_ROOM: "你未加入房间, 请创建或加入房间后参加21点游戏!",

    ERROR_ROOM_CHAT_NOT_OPEN: "房间功能暂未开放!",
}


class CommandError(Exception):
    """
    desc:   命令错误时，需要抛出的异常
    """
    pass


class ErrorMsg(Exception):
    pass


class ClientProtocol:
    """
    desc:   客户端协议，用于处理客户端输入的命令和来自服务器的数据
    """
    def __init__(self):
        self.__split_ch = ' '
        self.__func_dict = {}
        self.__init_func_dict()

    def __init_func_dict(self):
        """
        desc:   将命令类型和返回消息的类型，通过字典，绑定对应的处理函数
            好处是，消除if else语句，加快python解析的速度
        """
        self.__func_dict["register"] = self.__register_req
        self.__func_dict["login"] = self.__login_req
        self.__func_dict["logout"] = self.__logout_req
        self.__func_dict["chat"] = self.__chat_world_req
        self.__func_dict["chatto"] = self.__chat_to_req
        self.__func_dict["chatroom"] = self.__chat_room_req
        self.__func_dict["createroom"] = self.__create_room_req
        self.__func_dict["enterroom"] = self.__enter_room_req
        self.__func_dict["leaveroom"] = self.__leave_room_req
        self.__func_dict["queryroomlist"] = self.__query_room_list_req
        self.__func_dict["game21"] = self.__send_game21_expression_req
        self.__func_dict[REGISTER_ACK] = self.__register_ack
        self.__func_dict[LOGIN_ACK] = self.__login_ack
        self.__func_dict[LOGOUT_ACK] = self.__logout_ack
        self.__func_dict[WORLD_CHAT_ACK] = self.__chat_world_ack
        self.__func_dict[PRIVATE_CHAT_ACK] = self.__chat_to_ack
        self.__func_dict[ROOM_CHAT_ACK] = self.__chat_room_ack
        self.__func_dict[WORLD_CHAT_MSG_TRANSFER] = self.__transfer_chat_world_msg
        self.__func_dict[ROOM_CHAT_MSG_TRANSFER] = self.__transfer_chat_room_msg
        self.__func_dict[PRIVATE_CHAT_MSG_TRANSFER] = self.__transfer_chat_to_msg
        self.__func_dict[SYSTEM_MSG_INFORM] = self.__inform_system_msg
        self.__func_dict[CREATE_ROOM_ACK] = self.__create_room_ack
        self.__func_dict[ENTER_ROOM_ACK] = self.__enter_room_ack
        self.__func_dict[LEAVE_ROOM_ACK] = self.__leave_room_ack
        self.__func_dict[QUERY_ROOM_LIST_ACK] = self.__query_room_list_ack
        self.__func_dict[SEND_GAME21_EXPRESSION_ACK] = self.__send_game21_expression_ack

    def parse_snd(self, s):
        """
        desc:       分析发送的字符串数据, 解析成字典数据
        param:
            s:  待发送的原始命令
        return:
            dict:   字符串解析后的字典数据，不包含原始字符串中的命令类型
        exception:
            CommandError:   命令错误异常
        """
        strs = self.__split_str(s)  # 将输入的字符串以给定的分隔符（默认是空格）进行分割
        cmd_tpye = strs[0]
        if cmd_tpye not in self.__func_dict.keys():
            desc = "[系统]输入的命令错误, 不存在命令:" + cmd_tpye
            # print(desc)
            raise CommandError(desc)
        else:
            return self.__func_dict[cmd_tpye](strs[1:])

    def __split_str(self, s):
        """
        desc:       分割字符串
        param:
            s:  待分割的字符串
        return:
            list:   分割后的字符串列表
        """
        s = s.strip()
        tmp = s.split(self.__split_ch)
        res = []
        for string in tmp:
            if string:
                res.append(string)
        return res

    @staticmethod
    def __check_param_num(strs, name, num):
        """
        desc:       检测命令中的参数数量是否合法，对于register、login等命令，
            最后一个参数不一定是以分割符分割的，所以对于这类命令，不执行该检测，
            对于logout、leaveroom这些命令，参数数量固定，参数必须以分割符分割，
            则必须检测参数数量是否合法
            注意：参数数量不包含命令类型这个参数!
        param:
            strs:  待检测的参数列表
            name:   检测的参数列表对应的命令类型
            num:    正确的参数数量
        return: 空
        exception:  命令错误异常
        """
        if len(strs) != num:
            desc = "命令参数的数量错误, 命令[%s]只能有%d个参数!" % (name, num)
            raise CommandError(desc)


    '''
    注册请求协议:
    {
        option:int, name:str, pwd:str
    }
    '''
    def __register_req(self, strs):
        """
        desc:   处理注册请求的参数列表，返回注册请求的消息（字典）
        param:
            strs:   参数列表，不包含消息类型
        return:
            dict:   注册请求的消息
        """
        # 用户名、密码不可以包含空格!
        self.__check_param_num(strs, "register", 2)
        res = {}
        res["option"] = REGISTER_REQ
        res["name"] = strs[0]
        res["pwd"] = "".join(strs[1:])
        return res

    '''
    登录请求协议:
    {
        option:int, name:str, pwd:str
    }
    '''
    def __login_req(self, strs):
        """
        desc:   处理登录请求的参数列表，返回登录请求的消息（字典）
        param:
            strs:   参数列表，不包含消息类型
        return:
            dict:   登录请求的消息
        """
        # 用户名、密码不可以包含空格!
        self.__check_param_num(strs, "login", 2)
        res = {}
        res["option"] = LOGIN_REQ
        res["name"] = strs[0]
        res["pwd"] = strs[1]
        return res

    '''
    退出登录请求协议:
    {
        option:int
    }
    '''
    def __logout_req(self, strs):
        """
        desc:   处理登出请求的参数列表，返回登出请求的消息（字典）
        param:
            strs:   参数列表，不包含消息类型
        return:
            dict:   登出请求的消息
        """
        self.__check_param_num(strs, "logout", 0)
        res = {}
        res["option"] = LOGOUT_REQ
        return res

    '''
    世界聊天请求协议:
    {
        option:int, msg:str
    }
    '''
    def __chat_world_req(self, strs):
        """
        desc:   处理世界聊天的参数列表，返回世界聊天的消息（字典）
        param:
            strs:   参数列表，不包含消息类型
        return:
            dict:   世界聊天的消息
        """
        # self.__check_param_num(strs, "chat", 1)
        res = {}
        res["option"] = WORLD_CHAT_REQ
        res["context"] = " ".join(strs)     # 聊天的消息可能包含空格，因此需要将分割后的字符串拼接好
        return res

    '''
    私人聊天请求协议:
    {
        option:int, peername:str, msg:str
    }
    '''
    def __chat_to_req(self, strs):
        """
        desc:   处理私人聊天的参数列表，返回私人聊天的消息（字典）
        param:
            strs:   参数列表，不包含消息类型
        return:
            dict:   私人聊天的消息
        """
        #self.__check_param_num(strs, "chatto", 2)
        res = {}
        res["option"] = PRIVATE_CHAT_REQ
        res["peername"] = strs[0]
        res["context"] = " ".join(strs[1:])
        return res

    '''
    房间聊天请求协议:
    {
        option:int, msg:str
    }
    '''
    def __chat_room_req(self, strs):
        """
        desc:   处理房间聊天的参数列表，返回房间聊天的消息（字典）
        param:
            strs:   参数列表，不包含消息类型
        return:
            dict:   房间聊天的消息
        """
        #self.__check_param_num(strs, "chatroom", 1)
        res = {}
        res["option"] = ROOM_CHAT_REQ
        res["context"] = " ".join(strs)
        return res

    '''
    创建房间请求协议:
    {
        option:int, room_name:str
    }
    '''
    def __create_room_req(self, strs):
        """
        desc:   创建房间请求的参数列表，返回创建房间的消息（字典）
        param:
            strs:   参数列表，不包含消息类型
        return:
            dict:   创建房间的消息
        """
        res = {}
        res["option"] = CREATE_ROOM_REQ
        res["room_name"] = " ".join(strs)
        return res

    '''
    进入房间请求协议:
    {
        option:int, room_name:str
    }
    '''
    def __enter_room_req(self, strs):
        """
        desc:   进入房间请求的参数列表，返回进入房间的消息（字典）
        param:
            strs:   参数列表，不包含消息类型
        return:
            dict:   进入房间的消息
        """
        res = {}
        res["option"] = ENTER_ROOM_REQ
        res["room_name"] = " ".join(strs)
        return res

    '''
    离开房间请求协议:
    {
        option:int
    }
    '''
    def __leave_room_req(self, strs):
        """
        desc:   离开房间请求的参数列表，返回离开房间的消息（字典）
        param:
            strs:   参数列表，不包含消息类型
        return:
            dict:   离开房间的消息
        """
        self.__check_param_num(strs, "leaveroom", 0)
        res = {}
        res["option"] = LEAVE_ROOM_REQ
        return res

    '''
    21点游戏请求协议:
    {
        option:int
    }
    '''
    def __send_game21_expression_req(self, strs):
        """
        desc:   21点游戏请求的参数列表，返回21点游戏的消息（字典）
        param:
            strs:   参数列表，不包含消息类型
        return:
            dict:   21点游戏的消息
        """
        res = {}
        res["option"] = SEND_GAME21_EXPRESSION_REQ
        res["expression"] = " ".join(strs)
        return res

    '''
    查询房间列表请求协议:
    {
        option:int
    }
    '''
    def __query_room_list_req(self, strs):
        """
        desc:   查询房间列表请求的参数列表，返回查询房间列表的消息（字典）
        param:
            strs:   参数列表，不包含消息类型
        return:
            dict:   查询房间列表的消息
        """
        self.__check_param_num(strs, "queryroomlist", 0)
        res = {}
        res["option"] = QUERY_ROOM_LIST_REQ
        return res

    def parse_recv(self, dict_data):
        """
        desc:   解析反序列化后的服务器消息体，并执行对应的消息处理函数
        param:
            dict_data:   反序列化后的服务器消息体
        return: 空
        Exception:  返回的消息体不是字典时，报异常
        """
        if type(dict_data) is not dict:
            raise Exception("received msg must be dict!")
        if "ret" in dict_data and dict_data["ret"] != SUCCESS_OPERATION:
            #raise ErrorMsg(_ErrorCode[ret])
            print(("[系统]" + _ErrorCode[dict_data["ret"]]).decode("utf-8"))
        else:
            option = dict_data["option"]
            if option in self.__func_dict.keys():
                self.__func_dict[option](dict_data)

    @staticmethod
    def __register_ack(dict_data):
        print("[系统]注册成功!".decode("utf-8"))

    @staticmethod
    def __login_ack(dict_data):
        print("[系统]登录成功!".decode("utf-8"))

    @staticmethod
    def __logout_ack(dict_data):
        print("[系统]退出登录成功!")

    '''
    发送世界消息的确认:
    {
        option:int, ret:int
    }
    '''
    @staticmethod
    def __chat_world_ack(dict_data):
        ret = dict_data["ret"]
        if ret == SUCCESS_OPERATION:
            print("[系统]消息发送成功!".decode("utf-8"))
        else:
            print(("[系统]" + _ErrorCode[ret]).decode("utf-8"))

    '''
    发送房间消息的确认:
    {
        option:int, ret:int
    }
    '''
    @staticmethod
    def __chat_room_ack(dict_data):
        ret = dict_data["ret"]
        if ret == SUCCESS_OPERATION:
            print("[系统]消息发送成功!".decode("utf-8"))
        else:
            print(("[系统]" + _ErrorCode[ret]).decode("utf-8"))

    '''
    发送私人消息的确认:
    {
        option:int, ret:int
    }
    '''
    @staticmethod
    def __chat_to_ack(dict_data):
        ret = dict_data["ret"]
        if ret == SUCCESS_OPERATION:
            print("[系统]消息发送成功!".decode("utf-8"))
        else:
            print(("[系统]" + _ErrorCode[ret]).decode("utf-8"))

    '''
    接收别人发送的世界消息:
    {
        option:int, context:str, peername:str
    }
    '''
    @staticmethod
    def __transfer_chat_world_msg(dict_data):
        chat_msg = "[世界]%s:%s" % (dict_data["peername"], dict_data["context"])
        print(chat_msg.decode("utf-8"))

    '''
    接收别人发送的房间消息:
    {
        option:int, context:str, peername:str
    }
    '''
    @staticmethod
    def __transfer_chat_room_msg(dict_data):
        chat_msg = "[房间]%s:%s" % (dict_data["peername"], dict_data["context"])
        print(chat_msg.decode("utf-8"))

    '''
    接收别人发送的消息:
    {
        option:int, context:str, peername:str
    }
    '''
    @staticmethod
    def __transfer_chat_to_msg(dict_data):
        chat_msg = "[私聊]%s@你:%s" % (dict_data["peername"], dict_data["context"])
        print(chat_msg.decode("utf-8"))

    '''
    系统消息:
    {
        option:int, context:str
    }
    '''
    @staticmethod
    def __inform_system_msg(dict_data):
        msg = dict_data["context"]
        print(msg.decode("utf-8"))

    '''
    创建房间的确认:
    {
        option:int, context:str
    }
    '''
    @staticmethod
    def __create_room_ack(dict_data):
        ret = dict_data["ret"]
        if ret == SUCCESS_OPERATION:
            print("[系统]成功创建房间!".decode("utf-8"))
        else:
            print(("[系统]" + _ErrorCode[ret]).decode("utf-8"))

    '''
    进入房间的确认:
    {
        option:int, context:str
    }
    '''
    @staticmethod
    def __enter_room_ack(dict_data):
        ret = dict_data["ret"]
        if ret == SUCCESS_OPERATION:
            print("[系统]成功进入房间!".decode("utf-8"))
        else:
            print(("[系统]" + _ErrorCode[ret]).decode("utf-8"))

    '''
    离开房间的确认:
    {
        option:int, context:str
    }
    '''
    @staticmethod
    def __leave_room_ack(dict_data):
        ret = dict_data["ret"]
        if ret == SUCCESS_OPERATION:
            print("[系统]成功离开房间!".decode("utf-8"))
        else:
            print(("[系统]" + _ErrorCode[ret]).decode("utf-8"))

    '''
    离开房间的确认:
    {
        option:int, context:str
    }
    '''
    @staticmethod
    def __query_room_list_ack(dict_data):
        ret = dict_data["ret"]
        if ret == SUCCESS_OPERATION:
            room_list = dict_data["room_list"]
            if len(room_list) == 0:
                print("[系统]不存在房间!".decode("utf-8"))
            else:
                print(("房间数量:" + str(len(room_list))).decode("utf-8"))
                for i in range(len(room_list)):
                    room_id = i + 1 # 房间编号从1开始
                    room = room_list[i]
                    print((str(room_id) + ",房间名:" + room["room_name"] +
                          ", 房间人数:" + str(room["room_member_num"])).decode("utf-8"))
        else:
            print(("[系统]" + _ErrorCode[ret]).decode("utf-8"))

    '''
    21点游戏的确认:
    {
        option:int, context:str
    }
    '''
    @staticmethod
    def __send_game21_expression_ack(dict_data):
        ret = dict_data["ret"]
        if ret == SUCCESS_OPERATION:
            context = dict_data["context"]  # "[系统]成功参加游戏!"
            print(context.decode("utf-8"))
        else:
            print(("[系统]" + _ErrorCode[ret]).decode("utf-8"))


if __name__ == "__main__":
    pro = ClientProtocol()
    res = pro.parse_snd("register netease1 123")
    print(res)
    res = pro.parse_snd("login netease1 123")
    print(res)
    res = pro.parse_snd("chat 你好啊")
    print(res["msg"])
    res = pro.parse_snd("chatto netease2 你好啊")
    print(res)
    d = {"option": REGISTER_ACK, "ret": 0, "peer": "hello", "msg": "你好啊"}
    pro.parse_recv(d)